package wwk.common.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.Proxy;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import wwk.common.util.ByteUtil;
import android.os.AsyncTask;
import android.util.Log;

public class HttpEngine extends AsyncTask// implements Runnable
{
	public enum Method
	{
		GET,
		POST,
	}
	
	public final static String ENCODING_UTF8="utf-8";
	public final static String QUESTION="?";
	public final static String AND="&";
	
	private final static int MinBufferSize=1024*20;
	private int bufferSize=MinBufferSize;
	
	public HttpResponseInterface httpResponseInterface;			//����ص��ӿ�
	public HttpPostDataInterface httpPostDataInterface;
	
	//private HttpURLConnection httpURLConnection=null;	//http����,HttpClient
	private StringBuffer urlStr;//�����url
	private Method method;
	private Map<String,String> reqHeadMap;
	//private ByteArrayOutputStream postStream=null;
	//private byte[] postBytes;//��������			
	//private Thread thread=null;
	private boolean running;//�Ƿ�ȡ��
	
	private byte[] postData;//post�������ݣ���postDataΪ��ʱ�����getPostData()�ӿں����л�ȡ������ݡ�
	private int timeout;//milliseconds ����
	private String proxyAddress;//�������ӵ�ַ
	private int proxyPort;//�������Ӷ˿�

	private byte[] responsedBytes;//�ڻ���ʱ����ÿ�λ�ȡ�������;|������ģ��http��Ӧ�����;��
	private boolean responsedInProgress;//true������������ٷ��ء�false��ÿ����һ����ݶ��ص���
	
	//private int iapId;//�����id
	//private Context context;//��ȡapn��
	public int tag=0;//��ʶ

	public HttpEngine(HttpResponseInterface httpResponseInterface)
	{
		this.httpResponseInterface=httpResponseInterface;
		
		init();
	}
	
	public HttpEngine(HttpResponseInterface httpResponseInterface,int tag)
	{
		this.httpResponseInterface=httpResponseInterface;;
		this.tag=tag;
		
		init();
	}
	
	private void init()
	{
		reqHeadMap=new HashMap<String,String>();
		
		//Ĭ����Ӽ���ͷ,������Ը�������.
		reqHeadMap.put("Content-Type","application/octet-stream");// , multipart/form-data; boundary= , application/octet-stream , application/x-www-form-urlencoded
		reqHeadMap.put("Connection", "Keep-Alive");// ά�ֳ�����
		reqHeadMap.put("Charset", ENCODING_UTF8);
		
		timeout=30*1000;//30��
		method=Method.GET;
		
		responsedInProgress=false;
	}
	
	public void setProxy(String proxyAddress,int proxyPort)
	{
		this.proxyAddress=proxyAddress;
		this.proxyPort=proxyPort;
	}
	
	/**
	 * ����url
	 */
	public void setUrl(String urlStr)
	{
		this.urlStr=new StringBuffer(urlStr);
	}
	
	/**
	 * ����method
	 */
	public void setMethod(Method method)
	{
		this.method=method;
	}
	
	public void setTimeout(int timeout)
	{
		this.timeout = timeout;
	}
	
	/*
	public void setIapId(int iapId)
	{
		this.iapId = iapId;
	}
	*/

	public boolean isResponsedInProgress() 
	{
		return responsedInProgress;
	}

	public void setResponsedInProgress(boolean responsedInProgress) 
	{
		this.responsedInProgress = responsedInProgress;
	}
	
	public void setBufferSize(int bufferSize)
	{
		this.bufferSize=Math.max(bufferSize, MinBufferSize);
	}
	
	/**
	 * �������ͷ
	 */
	public void addRequestHead(String key,String value)
	{
		//if(!reqHeadMap.containsKey(key))
		reqHeadMap.put(key,value);//�Ḳ�Ǿ�ֵ
	}
	
	/**
	 * ����post���������
	 * @param postStream
	 */
//	public void setPostBytes(byte[] postBytes)
//	{
//		this.postBytes=postBytes;
//	}
	
	/**
	 * ����Ӧ��,ֱ�������ҷ������,����http����
	 */
	public void simulateRequest(byte[] dataForResponse)
	{
		responsedBytes=dataForResponse;
		request();
	}
	
	/**
	 * ����
	 */
	public void request()
	{
//		if(thread!=null)
//		{
//			thread.stop();
//		}
//		thread=new Thread(this);
//		thread.start();
		
		running=true;
		execute();
	}
	
	public void request(byte[] postData)
	{
		this.postData=postData;
		if(postData!=null)
			reqHeadMap.put("Content-Length",""+postData.length);
		
		running=true;
		execute();
	}
	
	public void request(String postString)
	{
		postData=allPostBytes(postString,null);//urlStr
		if(postData!=null)
			reqHeadMap.put("Content-Length",""+postData.length);
		
		//ע���ύ�?ʱ��reqHeadMap.put("Content-Type","application/x-www-form-urlencoded");
		
		
		running=true;
		execute();
	}
	
	/**
	 * ȡ��
	 */
	public void cancel()
	{
//		if(thread!=null)
//			thread.stop();
		running=false;
		super.cancel(true);
	}
	
	/**
	 * from Runnable
	 * 
	 * ʵ��'����-����'�߳�
	 */
	public void run()
	{
		if(responsedBytes!=null || urlStr==null)
		{
			//ģ��http��Ӧ
			httpResponseUncryptographic(HttpURLConnection.HTTP_OK,null,null,responsedBytes,responsedBytes.length,true);
			responsedBytes=null;
			return;
		}

		/*
		//proxy
		ApnNode node=ApnUtil.getApn(context, iapId);
		if(node!=null)
		{
			setProxy(node.getProxy(), node.getPort());
		}
		*/
		
		//1.����URL
		URL url=null;
		if(running)
		{
			try 
			{
				
				url = new URL(urlStr.toString());
			} 
			catch (MalformedURLException e1) 
			{
				//url��ʽ����
				e1.printStackTrace();
				//��Ӧ
				httpResponse(HttpURLConnection.HTTP_BAD_REQUEST,e1.getMessage(),null,null,0,true);
	
				running=false;
			}
		}
		
		//2.�õ�HttpURLConnection
		HttpURLConnection httpConn = null;
		if(running)
        {
			try
			{
				if(proxyAddress!=null && proxyAddress.length()>0)
				{
					//ʹ�ô���
					Proxy proxy=new Proxy(java.net.Proxy.Type.HTTP,new InetSocketAddress(proxyAddress,proxyPort));//"10.0.0.172",80
					httpConn = (HttpURLConnection) url.openConnection(proxy);
				}
				else
				{
					//��ʹ�ô���
					httpConn = (HttpURLConnection) url.openConnection();
				}
				
				//���ó�ʱ
				httpConn.setConnectTimeout(timeout);
				httpConn.setReadTimeout(timeout);

			} 
			catch (IOException e1)
			{
				//����ʧ��
				e1.printStackTrace();
				//��Ӧ
				httpResponse(HttpURLConnection.HTTP_BAD_REQUEST,e1.getMessage(),null,null,0,true);
				
				running=false;
			}
        } 
		
		if(running)
        {
			//3.�����������
			String methodStr="GET";
			if(method==Method.POST)
			{
				methodStr="POST";
				
				httpConn.setDoOutput(true);//ʹ�� URL ���ӽ������
				httpConn.setDoInput(true);//ʹ�� URL ���ӽ�������
				httpConn.setUseCaches(false);//���Ի���
			}
    
			//post��������
			try
			{
				httpConn.setRequestMethod(methodStr);//����URL���󷽷�
			}
			catch (ProtocolException e1)
			{
				//
				e1.printStackTrace();
				//��Ӧ
				httpResponse(HttpURLConnection.HTTP_BAD_METHOD,e1.getMessage(),null,null,0,true);
				
				running=false;
			}
			
			if(running)
			{
				if(reqHeadMap!=null)
				{
					Iterator<Entry<String,String>> iterator=reqHeadMap.entrySet().iterator();
					while(iterator.hasNext())
					{
						Entry<String,String> head=iterator.next();
						httpConn.setRequestProperty(head.getKey(),head.getValue());
					}
				}
			}
        }

		//4.����
		if(running)
        {
	        try
	        {
	        		httpConn.connect();
	        }
	        catch (IOException e1)
			{
				//����ʧ��
				e1.printStackTrace();
				Log.e("",e1.getMessage());
				//��Ӧ
				httpResponse(HttpURLConnection.HTTP_CONFLICT,e1.getMessage(),null,null,0,true);
				running=false;
			}
	        catch(IllegalArgumentException e2)//e2
	        {
	        	//����ʧ��
				e2.printStackTrace();
				//��Ӧ
				httpResponse(HttpURLConnection.HTTP_CONFLICT,e2.getMessage(),null,null,0,true);
				running=false;
	        }
	        catch(Exception e3)//e2	IllegalArgumentException  (id=830066430704)	
	        {
	        		//����ʧ��
	        		e3.printStackTrace();
				Log.e("",e3.getMessage());
				//��Ӧ
				httpResponse(HttpURLConnection.HTTP_CONFLICT,e3.getMessage(),null,null,0,true);
				running=false;
	        }
        }
        
        //5.�����������д�����
        if(running)
        {
        		//����Get����ʱ�����������д��ݡ�Post�����д��
	        if(method==Method.POST )//&& postBytes!=null)
			{
		        OutputStream outputStream = null;
				try
				{
					outputStream = httpConn.getOutputStream();
					if(postData==null)
					{
						if(httpPostDataInterface!=null)
						{
							byte[] postBytes=httpPostDataInterface.getPostData();
							while(postBytes!=null)
							{
								outputStream.write(postBytes);
								postBytes=httpPostDataInterface.getPostData();
							}
						}
					}
					else
					{
						outputStream.write(postData);
					}
					outputStream.flush();
					outputStream.close();
				}
				catch (IOException e1)
				{
					e1.printStackTrace();
					//��Ӧ
					httpResponse(HttpURLConnection.HTTP_BAD_METHOD,e1.getMessage(),null,null,0,true);
	
					running=false;
				}
			}//end if(method==Method.POST )
        }
		
		//6.��������������ȡ���	   
        if(running)
        {
			InputStream inputStream=null;
			try
			{
				inputStream = httpConn.getInputStream();
			}
			catch (IOException e1)
			{
				e1.printStackTrace();
				//��Ӧ HTTP_CLIENT_TIMEOUT HTTP_NOT_FOUND
				httpResponse(HttpURLConnection.HTTP_CLIENT_TIMEOUT,e1.getMessage(),null,null,0,true);

				running=false;
			}
			
			if(running)
			{
				byte[] buffer = new byte[bufferSize];
				int len = 0;
				//ByteArrayOutputStream responseBytes = new ByteArrayOutputStream();
				int contentLengthHasRead=0;//
				int totalContentLength=httpConn.getContentLength();
				try
				{
					if(totalContentLength==0)
					{
						//��Ӧ
						httpResponse(httpConn.getResponseCode(),null,httpConn,null,0,true);
					}
					else
					{
						boolean finish=false;
						
						//ѭ����ȡ���
						while ((len = inputStream.read(buffer)) >= 0)// contentLengthHasRead<responseContentLength && 
						{
							contentLengthHasRead+=len;
			
							if(totalContentLength==-1)
							{
								if(len<=0)
									finish=true;
							}
							else
							{
								if(contentLengthHasRead>=totalContentLength)
									finish=true;
							}
	
							byte[] readedByts=buffer;
							if(len<bufferSize)
							{
								readedByts=new byte[len];
								System.arraycopy(buffer, 0, readedByts, 0, len);
							}
							
							if(responsedBytes==null)
							{
								responsedBytes=readedByts;
							}
							else
							{
								responsedBytes=ByteUtil.comboBytesArray(responsedBytes,readedByts);
							}
	
							if(responsedInProgress)
							{
								if(responsedBytes.length>=bufferSize)
								{
									int errorCode=httpConn.getResponseCode();
									httpResponse(errorCode,null,httpConn,responsedBytes,totalContentLength,finish);
									responsedBytes=null;
								}
							}
							
							if(finish)
							{
								if(responsedBytes!=null)
								{
									int errorCode=httpConn.getResponseCode();
									httpResponse(errorCode,null,httpConn,responsedBytes,totalContentLength,finish);
								}
								
								break;
							}
							
							if(!running)
							{
								break;
							}
						}//end while
						
						if(!finish)
						{
							finish=true;
							
							int errorCode=httpConn.getResponseCode();
							httpResponse(errorCode,null,httpConn,responsedBytes,totalContentLength,finish);
						}
						
						inputStream.close();
					}
				}
				catch (IOException e1)
				{
					e1.printStackTrace();
					//��Ӧ
					httpResponse(HttpURLConnection.HTTP_CLIENT_TIMEOUT,e1.getMessage(),null,null,0,true);
				
					running=false;
				}
			}
        }
        
        if(httpConn!=null)
        {
        		httpConn.disconnect();
        }
	}
	
	private void httpResponse(int errorCode,String errorDesc,HttpURLConnection httpURLConnection,
			byte[] responseBytes,int contentLength,boolean responseFinish)
	{
		if(httpResponseInterface!=null && running)
		{
			String encoding=null;
			if(httpURLConnection!=null)
			{
				String contentType=httpURLConnection.getHeaderField("Content-Type");//text/html; charset=utf-8
				encoding=getEncoding(contentType);
			}
			
			HttpResponseData data=new HttpResponseData();
			data.errorCode=errorCode;
			data.errorDesc=errorDesc;
			data.httpURLConnection=httpURLConnection;
			data.bytes=responseBytes;
			data.contentLength=contentLength;
			data.encoding=encoding;
			data.cryptographic=true;
			data.responseFinished=responseFinish;
			data.tag=tag;
			
			httpResponseInterface.onHttpResponsed(data);
		}
		
		if(responseFinish)
		{
			running=false;
		}
	}
	
	private void httpResponseUncryptographic(int errorCode,String errorDesc,HttpURLConnection httpURLConnection,byte[] responseBytes,int bytesSize,boolean responseFinish)
	{
		if(httpResponseInterface!=null && running)
		{
			String encoding=null;
			if(httpURLConnection!=null)
			{
				String contentType=httpURLConnection.getHeaderField("Content-Type");//text/html; charset=utf-8
				encoding=getEncoding(contentType);
			}
			
			HttpResponseData data=new HttpResponseData();
			data.errorCode=errorCode;
			data.errorDesc=errorDesc;
			data.httpURLConnection=httpURLConnection;
			data.bytes=responseBytes;
			data.encoding=encoding;
			data.cryptographic=false;
			data.responseFinished=responseFinish;
			data.tag=tag;
			
			httpResponseInterface.onHttpResponsed(data);
		}
		
		if(responseFinish)
		{
			running=false;
		}
	}
	
//	/**
//	 * from AsyncTask
//	 */
//	protected void onPreExecute() 
//	{
//		Log.d("AsyncTask","onPreExecute");
//    }
	
	/**
	 * from AsyncTask
	 */
	@Override
	protected Object doInBackground(Object... params)
	{
		Log.d("AsyncTask","doInBackground");
		run();
		return null;
	}
	
//	/**
//	 * from AsyncTask
//	 */
//	protected void onProgressUpdate(Object... values) 
//	{
//		Log.d("AsyncTask","onProgressUpdate");
//    }
//	
	/**
	 * from AsyncTask
	 */
	protected void onPostExecute(Object result) 
	{
		Log.d("AsyncTask","onProgressUpdate");
    }
	
	/**
	 * from AsyncTask
	 */
	protected void onCancelled() 
	{
		running=false;
		Log.d("AsyncTask","onCancelled");
    }

	public boolean isRunning()
	{
		return running;
	}
	
	//��url�н�ȡ������ӵ�post����С�
	static private byte[] allPostBytes(String postStr,StringBuffer url)
	{
		byte[] bytesUtf8=null;
		try
		{
			if(postStr!=null)
				bytesUtf8 = postStr.getBytes(ENCODING_UTF8);
		}
		catch (UnsupportedEncodingException e)
		{
			e.printStackTrace();
		}

		byte[] bytesUuriParam=null;
		if(url!=null)
		{
			int pos=url.indexOf(QUESTION);
			if(pos>0 && pos<url.length()-1)
			{
				String uriParam=url.substring(pos+1);
				try
				{
					bytesUuriParam=uriParam.getBytes(ENCODING_UTF8);
				}
				catch (UnsupportedEncodingException e)
				{
					e.printStackTrace();
				}
				url.delete(pos,url.length());
			}
		}
		
		if(bytesUuriParam!=null)
		{
			if(bytesUtf8!=null)
			{
				byte[] char_and_bytes=null;
				try
				{
					char_and_bytes=AND.getBytes(ENCODING_UTF8);
				}
				catch (UnsupportedEncodingException e)
				{
					e.printStackTrace();
				}				
				bytesUtf8=ByteUtil.comboBytesArray(bytesUuriParam,char_and_bytes,bytesUtf8);
			}
			else
			{
				bytesUtf8=bytesUuriParam;
			}
		}
		
		return bytesUtf8;
	}
	
	private String getEncoding(String contentType)
	{
		//text/html; charset=utf-8
		if(contentType!=null)
		{
			int index=contentType.indexOf("=");
			if(index>=0 && contentType.length()>index+1)
			{
				return contentType.substring(index+1);
			}
		}
		return ENCODING_UTF8;
	}
}
